﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Lacrima.Framework.Infrastructure
{
    /// <summary>
    /// 文件保存枚举类型
    /// </summary>
    public enum SaveType
    {
        Add = 0,
        Update = 1,
        Delete = 2,
    }

    /// <summary>
    /// 文档生成服务类
    /// </summary>
    public static class DocumentGenerator
    {
        /// <summary>
        /// 保存配置
        /// </summary>
        /// <typeparam name="TSource">保存对象类型</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="directory">The directory.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="fileType">Type of the file.</param>
        public static void SaveModel<TSource>(this TSource source, string directory, string fileName, string fileType)
        {
            string file = Path.Combine(directory, string.Format("{0}.{1}", fileName, fileType));
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            using (FileStream stream = new FileStream(file, FileMode.Create, FileAccess.Write))
            {
                StreamWriter writer = new StreamWriter(stream);
                var xml = MyXmlSerializer.Serialize(typeof(TSource), source);
                writer.Write(xml);
                writer.Flush();
            }
        }

        /// <summary>
        /// 保存配置
        /// </summary>
        /// <typeparam name="TSource">保存对象类型</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="directory">The directory.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="fileType">Type of the file.</param>
        public static void Save<TSource>(this IEnumerable<TSource> source, string directory, string fileName, string fileType, SaveType saveType)
            where TSource : class,IEntityModel, new()
        {
            IEnumerable<TSource> result = source;
            string file = Path.Combine(directory, string.Format("{0}.{1}", fileName, fileType));
            if (!Directory.Exists(directory))
            {
                // 不存在文件目录则新建该文件目录
                Directory.CreateDirectory(directory);
            }
            else if (File.Exists(file) && saveType == SaveType.Add)
            {
                // 存在文件，则加载该文件，对文件中的数据进行load并与目标数据源进行merge操作
                result = LoadXML<TSource>(file);
                if (result != null && result.Any() && source != null && source.Any())
                {
                    // 查找source中不包含在result集合中的数据集合-要新增的数据
                    var notContainsList = source.Where(m => result.FirstOrDefault(n => n.Identify.Equals(m.Identify)) == null)
                                                .ToList();

                    // 将查询到的新数据集合新增到就数据集合中
                    result = result.Concat(notContainsList).ToList();
                }
            }
            else if (File.Exists(file) && saveType == SaveType.Delete)
            {
                result = LoadXML<TSource>(file);
                if (result != null && result.Any() && source != null && source.Any())
                {
                    // 查找result中不包含在source集合中的数据集合-result中要保留的数据
                    var notContainsList = result.Where(m => source.FirstOrDefault(n => n.Identify.Equals(m.Identify)) == null)
                                                .ToList();

                    // 将要保留的数据持久化保存即可
                    result = notContainsList;
                    if (result == null || !result.Any())
                    {
                        //领域模块性缓存中没有数据时删除该目录下的文件
                        File.Delete(file);
                    }
                }
            }

            if (result != null && result.Any())
            {
                using (FileStream stream = new FileStream(file, FileMode.Create, FileAccess.Write))
                {
                    StreamWriter writer = new StreamWriter(stream);
                    var xml = CreateXml(result);
                    writer.Write(xml);
                    writer.Flush();
                    writer.Close();
                }
            }
        }

        /// <summary>
        /// 清空文件目录
        /// </summary>
        /// <param name="directory">路径</param>
        /// <param name="recursive">是否执行递归删除</param>
        public static void ClearDirectory(string directory, bool recursive = true)
        {
            if (Directory.Exists(directory))
            {
                Directory.Delete(directory, recursive);
            }
        }

        /// <summary>
        /// 加载XML文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filePath">文件路径</param>
        /// <returns>List{``0}.</returns>
        public static List<T> LoadXML<T>(string filePath)
            where T : class,new()
        {
            string directory = filePath.Substring(0, filePath.LastIndexOf("\\"));
            if (Directory.Exists(directory) && File.Exists(filePath))
            {
                XElement xml = XElement.Load(filePath);
                var list = xml.Elements(typeof(T).Name).Select(o => o.XElementToEntity<T>())
                                                       .ToList();
                return list;
            }
            else
            {
                return null;
            }
        }

        /// <summary>  
        /// 创建图书XML文档（创建）  
        /// </summary>  
        private static string CreateXml<T>(IEnumerable<T> source)
        {
            //创建XML文档  
            XDocument doc = new XDocument();
            //创建声明对象  
            XDeclaration xDeclaration = new XDeclaration("1.0", "utf-8", "yes");
            doc.Declaration = xDeclaration;    //指定XML声明对象  
            //创建bookstore节点  
            XElement xElement = new XElement(typeof(T).Name + "s");

            PropertyInfo[] properties = typeof(T).GetProperties();

            foreach (var item in source)
            {
                //创建book节点  
                XElement node = new XElement(typeof(T).Name);

                foreach (var property in properties)
                {
                    //添加属性  
                    node.Add(new XElement(property.Name, property.GetValue(item)));
                }
                //将book节点添加到bookstore节点中  
                xElement.Add(node);
            }
            //保存文件  
            doc.Add(xElement);
            return doc.ToString();
        }

        private static T XElementToEntity<T>(this XElement source)
            where T : class,new()
        {
            PropertyInfo[] properties = typeof(T).GetProperties();
            T entity = new T();
            var nodes = source.Nodes().ToList();
            foreach (var property in properties)
            {
                var node = nodes.FirstOrDefault(o => o != null && o.GetName().Equals(property.Name));
                if (node != null && node.GetValue(property.Name) != null)
                {
                    var value = node.GetValue(property.Name);
                    property.SetValue(entity, Convert.ChangeType(value, property.PropertyType));
                }
            }
            return entity;
        }

        private static string GetName(this XNode node)
        {
            string source = node.ToString();
            return source.Substring(1, source.IndexOf(">") - 1);
        }

        // 此处可能有问题，以后在处理#caution 2016年12月30日11:21:09
        private static string GetValue(this XNode node, string name)
        {
            string source = node.ToString().Trim();
            string nodeName = node.GetName();
            if (nodeName.Equals(name))
            {
                return source.Replace(name, string.Empty)
                             .Replace("<>", string.Empty)
                             .Replace("</>", string.Empty);
            }
            return string.Empty;
        }
    }
}
